#include "shm.h"
#include "../error.h"

/*--------------------------------------------------------------------------*/

static char errorMsg[255];

/*--------------------------------------------------------------------------*/

static int attachShm(int shmId, int flags, void **ptr);

static int createMemShm(int key, int size, int flags, void **ptr);

static int openMemShm(int key, int size, int flags, void **ptr);

/*--------------------------------------------------------------------------*/

int
openShm(int key, int size)
{
	int error = ERROR_OK;
	
	if( (error = shmget(key, size, 0)) == -1)
	{	
		if( errno == EACCES ) /* No permission */
		{	
			perror("shmADT: Opening memory, acces denied, check memory permissions.\n");
			error = ERROR_ACCES;
		}
		else if( errno == ENOENT ) /* No shared memory exists */
		{	
			sprintf(errorMsg,"shmADT: Opening memory, memory block does not exists, key = %d \n", key);
			perror(errorMsg);
			error = ERROR_EXIST;
		}
		else /* Not enough memory or system limit reached */
		{	
			perror("shmADT: Opening memory, unexpected error.\n");
			error = ERROR_UNEX;
		}
	}
	
	return error;
}

/*--------------------------------------------------------------------------*/

int
createShm(int key, int size, int perms)
{
	int error = ERROR_OK;
	
	if( (error = shmget( (key_t)key, size, IPC_CREAT | perms /*| IPC_EXCL*/)) == -1)
	{	
		if( errno == EEXIST ) /* Queue already exists */
		{	
			sprintf(errorMsg,"shmADT: Creating memory, memory block already exists, key = %d \n", key);
			perror(errorMsg);
			error = ERROR_EXIST;
		}
		else /* Not enough memory or system limit reached */
		{	
			perror("shmADT: Creating memory, unexpected error.\n");
			error = ERROR_UNEX;
		}
	}
	
	return error;
}

/*--------------------------------------------------------------------------*/

int
delShm(int shmId)
{
	int error = ERROR_OK;
	
	error = shmctl(shmId, IPC_RMID, NULL);
	if(  error == -1)
	{
		if( errno == EACCES )
		{
			perror("shmADT: Deleting, acces denied, check shared memory permissions.\n");
			error = ERROR_ACCES;
		}
		else
		{
			perror("shmADT: Deleting, unexpected error.\n");
			error = ERROR_UNEX;
		}
	}

	return error;
}

/*--------------------------------------------------------------------------*/

static int
attachShm(int shmId, int flags, void **ptr)
{
	void *ans;
	int error = ERROR_OK;
	
	if( (ans = shmat( shmId, NULL, flags)) == (void *)-1)
	{	
		if( errno == EACCES ) /* No permission */
		{	
			perror("shmADT: Attaching memory, acces denied, check memory permissions.\n");
			error = ERROR_ACCES;
		}
		else /* Not enough memory or system limit reached */
		{	
			perror("shmADT: Attaching memory, unexpected error.\n");
			error = ERROR_UNEX;
		}
		*ptr = NULL;
	}
	else
	{
		*ptr = ans;
	}
	
	return error;
}

/*--------------------------------------------------------------------------*/

/* Read only */
int
attachShmR(int shmId, void **ptr)
{
	return attachShm(shmId, SHM_RDONLY, ptr);
}

/*--------------------------------------------------------------------------*/

/* Read and write */
int
attachShmRW(int shmId, void **ptr)
{
	return attachShm(shmId, 0, ptr);
}

/*--------------------------------------------------------------------------*/

int
detachShm(void *ptr)
{
	int error = ERROR_OK;
	
	if( shmdt( ptr ) == -1)
	{	
		perror("shmADT: Detaching memory, unexpected error.\n");
		error = ERROR_UNEX;
	}
	
	return error;
}

/*--------------------------------------------------------------------------*/

/* Create and attach */
static int
createMemShm(int key, int size, int flags, void **ptr)
{
	int ans;
	int error = ERROR_OK;
	
	error = createShm(key, size, flags);
	if( error < 0 )
		return error;
	ans = error;
	
	error = attachShm(ans, flags, ptr);
	if( error != ERROR_OK )
	{
		delShm(ans);
		return error;
	}
	
	return ans;
}

/*--------------------------------------------------------------------------*/

int
createMemShmR(int key, int size, int perms, void **ptr)
{
	return createMemShm(key, size, SHM_RDONLY | perms, ptr);
}

/*--------------------------------------------------------------------------*/

int
createMemShmRW(int key, int size, int perms, void **ptr)
{
	return createMemShm(key, size, perms, ptr);
}

/*--------------------------------------------------------------------------*/

static int
openMemShm(int key, int size, int flags, void **ptr)
{
	int ans;
	int error = ERROR_OK;

	error = openShm(key, size);
	if( error < 0 )
		return error;
	ans = error;
	
	error = attachShm(ans, flags, ptr);
	if( error != ERROR_OK )
	{
		return error;	
	}
	
	return ans;
}

/*--------------------------------------------------------------------------*/

int
openMemShmR(int key, int size, void **ptr)
{
	return openMemShm(key, size, SHM_RDONLY, ptr);
}

/*--------------------------------------------------------------------------*/

int
openMemShmRW(int key, int size, void **ptr)
{
	return openMemShm(key, size, 0, ptr);
}

/*--------------------------------------------------------------------------*/

int
delCreatedMemShm(int shmId, void *ptr)
{
	detachShm(ptr);
	delShm(shmId);
	return ERROR_OK;
}

/*--------------------------------------------------------------------------*/

int
delOpenedMemShm(void *ptr)
{
	return detachShm(ptr);
}

/*--------------------------------------------------------------------------*/
